package com.mfz.common.persistence

import com.mfz.common.dbHelper.*
import com.mfz.common.units.ID
import org.apache.logging.log4j.LogManager
import java.sql.ResultSet


internal fun getHelper(): Helper {
	return if (containsThreadDBHelper()) dbHelper else useAloneDBHelper
}

/**
 * 持久化对象实例的工厂接口.
 * <p>
 * 每一个对象均会实现一个继承类，用以获得对象实例。
 * <p>
 * 实现类做为一个单例存在，并且在构造函数当中设置对象类的表名/字段名等信息。并且实现{@link #createObject(User)}方法，
 * 并返回相对应的对象实例。
 * <p>
 * 使用此对象设置的表必须有主键，并且使用{@link #addField(String, Class, Object, boolean, boolean)}方法设置字段主键。
 * <p>
 * 继承类可修改对象操作过程当中执行的语句，{@link #setWherePrimaryKeys()}方法默认被其他方法调用。
 * <p>
 * 调用{@link #getObject(String, Object)}方法得到一个存在的对象。如此对象不存在，将返回null.调用{@link #getNewObject(User)}
 * 方法得到一个新的对象。
 * <p>
 * 每次获得一个对象时会将此对象进行缓存，使用{@link #setIsCheck(boolean)}可设置是否需要缓存，默认为true.
 */
interface Factory<out T : Persistence> {
	val tableName: String
	val relationship: Relationship?
	val fields: MutableList<Field>

	/**
	 * 根据给定的列名与值,给出相应的对象。
	 */
	fun get(name: String, value: Any): T?

	/**
	 * 根据给定的列名与值,给出相应的对象。
	 */
	fun get(values: Map<String, Any>): T?

	/**
	 * 返回一个新的对象,此对象为空对象
	 */
	fun getNewObject(): T

	/**
	 * 设置此对象是否可以被缓存,默认不进行缓存
	 */
	var useBuffer: Boolean

	/**
	 *根据指定名称,返回此列的信息
	 */
	fun getField(name: String): Field

	fun getPersistences(where: String): List<T>
}

abstract class AbstractFactory<out T : AbstractPersistence>(final override val tableName: String, final override val relationship: Relationship? = null) : Factory<T> {

	final override val fields: MutableList<Field> = if (containsThreadDBHelper()) {
		val result = dbHelper.getTableInfo(tableName)
		dbHelper.close()

		result
	} else {
		useAloneDBHelper.getTableInfo(tableName)
	}

	internal val pks: ArrayList<Field> = ArrayList()
	internal var sequence: Field? = null
	private val data = hashMapOf<Int, T>()
	private var selectSql: String = ""
	/**
	 * 指明当前对象是否使用缓存
	 */
	override var useBuffer: Boolean = false

	companion object {
		val log = LogManager.getLogger(AbstractFactory::class.java.name)!!
	}

	init {
		for (field in fields) {
			if (field.isPrimary) {
				pks.add(field)
			}
			if (field.isSequence) {
				sequence = field
			}
		}

		reSetSelectSql()
	}


	override fun getPersistences(where: String): List<T> {
		return getHelper().queryBySet(selectSql + where) {
			val result = ArrayList<T>()
			while (it.next()) {
				result.add(get(ID, it.getObject(ID))!!)
			}

			return@queryBySet result
		} ?: error("这里就不会进入来，还是保护一下的好")
	}

	private fun reSetSelectSql() {
		val builder = StringBuilder("select ")

		for (field in fields) {
			builder.append(field.name).append(",")
		}

		selectSql = builder.delete(builder.length - 1, builder.length).append(" from $tableName  t where ").toString()
	}

	fun addViewField(name: String, type: ColumnType) {
		fields.add(Field(name, type, false, "", "", isPrimary = false, isSequence = false, isView = true))
	}

	internal fun find(name: String): Int {
		return fields.indices.firstOrNull { fields[it].name == name } ?: -1
	}

	private fun findProduct(name: String, value: Any): T? {
		if (this.useBuffer && pks.size == 1 && pks.first().name == name) {
			return data[value]
		}

		return null
	}


	private fun findProduct(set: ResultSet): T? {
		var found = false

		if (this.useBuffer) {
			data.forEach { (_, v) ->
				for (pk in pks) {
					if (set.getObject(pk.name) != v[pk.name]) {
						continue
					}

					found = true
					break
				}

				if (found)
					return v
			}
		}

		return null
	}

	override fun get(values: Map<String, Any>): T? {
		val thisHelper = getHelper()
		val where = buildString {
			values.forEach {
				if (length > 0) append(" and ")
				append(" t.").append(it.key).append(" = ").append(thisHelper.format(it.value))
			}
		}

		var result: T? = null
		thisHelper.query("$selectSql $where") {
			result = if (useBuffer) findProduct(it) else null
			if (result !== null) {
				result = createNewObject()
				result!!.setFieldDataByDB(it)
				holdProduct(result!!)
			}
		}

		if (relationship != null) {
			result!!.loadAssociated()
		}

		return result
	}

	override fun get(name: String, value: Any): T? {
		if (find(name) < 0) {
			log.error("在数据库表:$tableName 中未定义此字段名:$name")
		}

		var result = findProduct(name, value)
		if (result != null)
			return result

		val helper = getHelper()
		helper.query("$selectSql t.$name = ${helper.format(value)}") {
			result = createNewObject()
			result!!.setFieldDataByDB(it)
			holdProduct(result!!)
		}

		if (relationship != null) {
			result!!.loadAssociated()
		}

		return result
	}

	private fun holdProduct(product: T): T {
		if (this.useBuffer && !data.containsKey(product[ID] ?: -1)) {
			data[product[ID]!!] = product
		}

		return product
	}

	fun getDatas(): List<T> {
		return data.values.toMutableList()
	}

	override fun getNewObject(): T {
		return createNewObject()
	}

	protected abstract fun createNewObject(): T


	override fun getField(name: String): Field {
		return fields[find(name)]
	}

}

